/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License.You may obtain a copy of the License at
	http://www.mozilla.org/MPL/MPL-1.1.html
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/

import com.bourre.commands.CommandManagerFPS;
import com.bourre.commands.Delegate;
import com.bourre.data.libs.AbstractLib;
import com.bourre.data.libs.ConfigLoader;
import com.bourre.data.libs.ConfigLoaderEvent;
import com.bourre.data.libs.LibEvent;
import com.bourre.data.libs.XMLToObjectDeserializer;
import com.bourre.events.IEvent;
import com.bourre.ioc.bean.BeanFactory;

import fever.data.libs.BitmapLib;
import fever.exception.IllegalAccessException;
import fever.ioc.conf.CoreConfiguration;
import fever.ioc.events.PluginEvent;
import fever.ioc.manager.AbstractManagerPlugin;
import fever.ioc.manager.events.PluginManagerEventList;
import fever.ioc.plugin.FeverPlugin;
import fever.ioc.plugin.PluginInfo;

/**
 * Extends <strong>{@link fever.ioc.plugin.FeverPlugin}</strong> to add 
 * managing system.
 * 
 * <p>When the plugin is ready ( configuration loaded or not ) an external 
 * event is disptached to tell the manager that a new plugin is available and 
 * ready to be use.
 * 
 * <p>Manager properties are defined in 
 * {@link fever.ioc.conf.CoreConfiguration} static class.
 * <ul>
 *   <li>managerChannel</li>
 *   <li>managerID</li>
 * </ul>
 * 
 * <p>In current state, only one manager can be defined in a context.
 * 
 * <p>Take a look at {@code fever.ioc.manager} package to see Plugin 
 * Manager implementation.
 * 
 * //TODO Documentation of basic Plugin xml file definition.
 * 
 * @author Romain Ecarnot
 */
class fever.ioc.plugin.RegistredPlugin extends FeverPlugin
{
	//-------------------------------------------------------------------------
	// Private properties
	//-------------------------------------------------------------------------
	
	private static var __CHECKER_NAME : String = '__MPc_';
	
	private var _bConfigEnabled : Boolean;	private var _bQuiteMode : Boolean;
	private var _oInfo : PluginInfo;
	
	
	//-------------------------------------------------------------------------
	// Public Properties
	//-------------------------------------------------------------------------
	
	/** Default name for information file. */
	public static var PLUGIN_INFORMATION_FILE : String = 'plugin.xml';
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Returns plugin path ( or {@code null} if not defined or used ).
	 */
	public function getPluginManagerChannel(  ) : String 
	{
		return CoreConfiguration.managerChannel;
	}
	
	/**
	 * Returns plugin path ( or {@code null} if not defined or used ).
	 */
	public function getPluginManagerID(  ) : String 
	{
		return CoreConfiguration.managerID;
	}
	
	/**
	 * Returns {@code true} if plugin configuration must loaded.
	 */
	public function isConfigEnabled(  ) : Boolean
	{
		return _bConfigEnabled;
	}
	
	/**
	 * Returns plugin's informations.
	 */
	public function getInformation( ) : PluginInfo
	{
		return _oInfo;
	}
	
	/**
	 * Triggered when the Plugin Manager want to activate this plugin.
	 * 
	 * <p>If configuration is not loaded, automatically loads it before.
	 * 
	 * <p>Overrides methods to implements your own behaviour.
	 */
	public function onActivatePlugin( event : IEvent ) : Void
	{
		if( !isConfigEnabled() || ( isConfigEnabled() && isConfigLoaded() ) )
		{
			_activatePlugin( );
		}
		else if( isConfigEnabled() ) _loadConfig();
	}
	
	
	//-------------------------------------------------------------------------
	// Private implementation
	//-------------------------------------------------------------------------
		
	/**
	 * Constructor.
	 * 
	 * @param infoEnabled {@code true} to enable information loading.
	 * @param bypassManager ( optional ) Pass {@code false} to disallow Manager 
	 * registration for this plugin. ( default is {@code true} of course ).
	 */ 
	private function RegistredPlugin( infoEnabled : Boolean, bypass : Boolean ) 
	{
		super( false );
		
		_bConfigEnabled = false;		_bQuiteMode = bypass;
		
		_processInformation( infoEnabled );
	}
	
	/**
	 * Overrides to not start configuration now.
	 * 
	 * <p>Process plugin information first.
	 */
	private function _processConfiguration( loadConfig : Boolean ) : Void 
	{
		//Do nothing
	}
	
	/**
	 * Process information loading.
	 */
	private function _processInformation( infoEnabled : Boolean ) : Void 
	{
		if ( infoEnabled ) _loadPluginInformation();
		else _setDefaultPluginInformation();
	}
	
	/**
	 * Overrides to custom xml file url.
	 */
	private function _getInfoFilePath(  ) : String 
	{
		return ( getConfigPath() + PLUGIN_INFORMATION_FILE );
	}
	
	private function _loadPluginInformation(  ) : Void 
	{
		XMLToObjectDeserializer.DESERIALIZE_ATTRIBUTES = true;
		XMLToObjectDeserializer.PUSHINARRAY_IDENTICAL_NODE_NAMES = true;
		
		var infoLoader : AbstractLib = _getInfoLoader( new Object() );
		infoLoader.setAntiCache( true );
		infoLoader.setTimeOut( 1000 );
		infoLoader.addEventListener( 
			AbstractLib.onLoadInitEVENT, 
			this, 
			_onInfoLoaded 
		);
		infoLoader.addEventListener( 
			AbstractLib.onErrorEVENT, 
			this, 
			_onInfoError 
		);
		infoLoader.addEventListener( 
			AbstractLib.onTimeOutEVENT, 
			this, 
			_onInfoError 
		);
		
		infoLoader.load( _getInfoFilePath() );
	}
	
	private function _setDefaultPluginInformation(  ) : Void 
	{
		_oInfo = _buildPluginInfo( null );
		
		_registerPlugin();
	}
	
	private function _onInfoLoaded( event : ConfigLoaderEvent ) : Void 
	{
		var vo : Object = event.getConfig();
		
		_oInfo = _buildPluginInfo( vo );
		_bConfigEnabled = _getConfigEnabled( vo );
		
		var startup : Boolean = _getStartupEnabled( vo );
		
		getLogger().warn( 'Configuration state for ' + getName() + 
			' -> ' + _bConfigEnabled + ' / ' + startup );		
		if( vo.thumb.url )
		{
			var name : String = getName() + '_thumb';
			var lib : BitmapLib = new BitmapLib( );
			lib.setName( getName() + '_thumb' );
			lib.addEventListener( 
				BitmapLib.onLoadInitEVENT, 
				this, 
				_onInfoComplete, 
				startup 
			);			lib.addEventListener( 
				BitmapLib.onErrorEVENT, 
				this, 
				_onInfoComplete, 
				startup 
			);			lib.addEventListener( 
				BitmapLib.onTimeOutEVENT, 
				this, 
				_onInfoComplete, 
				startup 
			);
			lib.load( getConfigPath() + vo.thumb.url );
		}
		else
		{
			_onInfoComplete( null, startup );
		}
	}
	
	/**
	 * Returns if configuration is necessary for this plugin.
	 * 
	 * <p>Feels free to override to match your information file structure.
	 */
	private function _getConfigEnabled( o : Object ) : Boolean 
	{
		return ( o.config.enabled == 'true' );	
	}
	
	/**
	 * Returns if configurationmust be loaded at startup.
	 * 
	 * <p>Feels free to override to match your information file structure.
	 */
	private function _getStartupEnabled( o : Object ) : Boolean 
	{
		return ( o.config.startup == 'true' );	
	}
	
	private function _onInfoComplete( event : LibEvent, bs : Boolean ) : Void 
	{
		if( event ) _oInfo.setThumbailID( event.getLib().getName() );
		
		if( _bConfigEnabled &&  bs )
		{
			_loadConfig();
		}
		else _registerPlugin();
	}
	
	private function _onInfoError( event : ConfigLoaderEvent ) : Void 
	{
		getLogger().fatal( _sName + ' informations failed' );
		
		_setDefaultPluginInformation();
	}
	
	/**
	 * Returns basic info loader as {@code null} is passed to 
	 * constructor.
	 * 
	 * <p>Feel free to override to enable custom or secure config loading.
	 */
	private function _getInfoLoader( object : Object ) : AbstractLib 
	{
		return new ConfigLoader( object );
	}
	
	private function _onConfigError( event : ConfigLoaderEvent ) : Void 
	{
		getLogger().fatal( getName() + ' configuration failed' );
		
		_registerPlugin();
	}
	
	/**
	 * Feels free to override with method to custom actions when config 
	 * is loaded.
	 * 
	 * <p>Generally used when config is not directly loaded throw constructor 
	 * parameter.
	 */
	private function _onConfigComplete( ) : Void 
	{
		_bConfigLoaded = true;
		
		_registerPlugin( );
	}
	
	/**
	 * Creates basic {@link fever.ioc.plugin.PluginInfo} information 
	 * instance.
	 * 
	 * <p>Overrides to enable new informations structure
	 */
	private function _buildPluginInfo( vo : Object ) : PluginInfo 
	{
		return new PluginInfo( this, vo );
	}
	
	/**
	 * Registers current plugin into Plugin's Manager.
	 * 
	 * <p>If manager does not exist yet, use 
	 * a <strong>CommandManagerFPS}</strong> command to wait for manager.
	 * 
	 * <p>if quite mode is enabled, bypass the manager detection.
	 */
	private function _registerPlugin() : Void
	{
		if( _bQuiteMode ) 
		{
			run();
			return;	
		}
		
		if( getPluginManagerChannel() != null && 
			getPluginManagerChannel() != getChannel() )
		{
			CommandManagerFPS.getInstance().pushWithName( 
				new Delegate( this, _checkPluginManager ), 
				__CHECKER_NAME + getName() 
			);
		}
		else run( );
	}
	
	private function _checkPluginManager(  ) : Void 
	{
		if( _isManagerAvailable() )
		{
			CommandManagerFPS.getInstance().removeWithName( 
				__CHECKER_NAME + getName() 
			);
			
			fireExternalEvent( 
				new PluginEvent( 
					PluginManagerEventList.onRegisterPluginEVENT, 
					_oInfo 
				), 
				getPluginManagerChannel() 
			);
			
			run( );
		}
	}
	
	private function _isManagerAvailable(  ) : Boolean
	{
		var hasChannel : Boolean = _oABExternal.hasChannelDispatcher( 
			getPluginManagerChannel() 
		);
		
		var isReady : Boolean = false;
		
		if( BeanFactory.getInstance().isRegistered( getPluginManagerID() ) )
		{
			isReady = AbstractManagerPlugin( 
				BeanFactory.getInstance().locate( getPluginManagerID() ) 
			).isManagerAvailable();
		}
		
		return ( hasChannel && isReady );
	}
	
	private function _activatePlugin(  ) : Void 
	{
		throw new IllegalAccessException( 
			'#_activatePlugin() must be overrided in ' + this 
		);	
	}
}